# -*- coding: utf-8 -*-
# @version        : 1.0
# @Create Time    : 2024/07/16 16:44
# @File           : validate.py
# @IDE            : PyCharm
# @desc           : 用户TOKEN凭证验证装饰器
from datetime import datetime
from utils import status
from typing import Optional, List, Union
from fastapi import Request, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel, computed_field
from application import settings
from application.settings import CONFIG
from core.database import db_getter
from core.exception import CustomException
from core.api_client import ApiClient
from core.logger import logger


class User(BaseModel):
    userid: Union[str, int]  # 工号
    username: str  # 姓名
    userCode: Optional[Union[str, int]] = None
    userAccount: str
    employeeId: Optional[Union[str, int]] = None
    employeeCode: Optional[Union[str, int]] = None
    roleList: Optional[List[dict]] = []
    permissions: Optional[List[str]] = []

    @computed_field
    def user_str(self) -> str:
        return f"{self.username}({self.userCode})"

    @computed_field
    def user_str_time(self) -> str:
        return f"{self.username}({self.userCode}) {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"


class Auth(BaseModel):
    user: User = None
    db: AsyncSession

    class Config:
        # 接收任意类型
        arbitrary_types_allowed = True


class AuthValidation:
    """
    用于用户每次调用接口时，验证用户提交的token是否正确，并从token中获取用户信息
    """
    error_code = status.HTTP_401_UNAUTHORIZED
    warning_code = status.HTTP_ERROR

    # status_code = 403 时，表示强制要求重新登录，因无系统权限，而进入到系统访问等问题导致

    @classmethod
    async def validate_token(cls, request: Request) -> User:
        """
        验证用户 token
        """
        if not request.cookies.get(settings.COOKIE_KEY):
            raise CustomException(
                msg="请您先登录！",
                code=status.HTTP_403_FORBIDDEN,
                status_code=status.HTTP_403_FORBIDDEN
            )
        try:
            resp = await ApiClient(CONFIG.AUTH.TOKEN_VALIDATE_TRIGGER, headers=request.headers).request()
            if resp.get("code") == -1:
                raise CustomException(
                    msg="未认证，请您重新登录",
                    code=status.HTTP_403_FORBIDDEN,
                    status_code=status.HTTP_403_FORBIDDEN
                )
        except RuntimeError as e:
            logger.exception(f"调用外部鉴权接口异常：{e}")
            raise CustomException(
                msg="调用外部鉴权接口异常，请联系管理员排查",
                code=status.HTTP_401_UNAUTHORIZED,
                status_code=status.HTTP_401_UNAUTHORIZED
            )
        except CustomException:
            raise
        except Exception as e:
            logger.exception(f"接口鉴权失败：{e}")
            raise CustomException(
                msg="接口鉴权失败，请联系管理员排查",
                code=status.HTTP_401_UNAUTHORIZED,
                status_code=status.HTTP_401_UNAUTHORIZED
            )
        return User(**resp['data'])

    @classmethod
    async def validate_user(cls, request: Request, user: User, db: AsyncSession) -> Auth:
        """
        验证用户信息
        :param request:
        :param user:
        :param db:
        :return:
        """
        if user is None:
            raise CustomException(msg="未认证，请您重新登陆", code=cls.error_code, status_code=cls.error_code)
        request.scope["userid"] = user.userid
        request.scope["username"] = user.username
        try:
            request.scope["body"] = await request.body()
        except RuntimeError:
            request.scope["body"] = "获取失败"
        return Auth(user=user, db=db)

    @classmethod
    def get_user_permissions(cls, user: User) -> set:
        """
        获取员工用户所有权限列表
        :param user: 用户实例
        :return:
        """
        return user.permissions


class AllUserAuth(AuthValidation):
    """
    如果有权限，那么会验证该用户是否包括权限列表中的其中一个权限
    """

    def __init__(self, permissions: List[str] = None):
        if permissions:
            self.permissions = set(permissions)
        else:
            self.permissions = None

    async def __call__(
            self,
            request: Request,
            db: AsyncSession = Depends(db_getter)
    ) -> Auth:
        """
        每次调用依赖此类的接口会执行该方法
        """
        if not settings.OAUTH_ENABLE:
            return Auth(db=db)
        user = await self.validate_token(request)
        result = await self.validate_user(request, user, db)
        permissions = self.get_user_permissions(user)
        if permissions != {'*.*.*'} and self.permissions:
            if not (self.permissions & permissions):
                raise CustomException(msg="无权限操作", code=status.HTTP_403_FORBIDDEN)
        return result
